﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Engine;
using Engine.Graphics;
using Engine.Physics;

namespace Playtime
{
    struct tData
    {
        public Boolean filled;
        public Color color;

        public tData(Color _color)
        {
            color = _color;
            filled = true;
        }
    }

    public struct Platform
    {
        public Vector2 start;
        public Vector2 end;

        public Platform(Vector2 s, Vector2 e)
        {
            start = s;
            end = e;
        }
    }

    public class LevelData
    {
        public List<Platform> Platforms;
        public LevelData()
        {
            Platforms = new List<Platform>();
        }

        public LevelData(List<Platform> _platforms)
        {
            Platforms = _platforms;
        }
    }

    public class LevelEditor : Component
    {
        #region Fields
        public static int cellSize = 350;

        public int mapSizeX = 4;
        public int mapSizeY = 4;

        public int selectedLayer = 1;

        public Texture2D[][,] layerTexture;
        public Color[][,][] layerData;
        public bool[][,] layerEdited;

        public List<Platform> platforms;

        private Vector2 dragStart;

        private bool drawing = false;

        public int selectedTool = 0;

        public Color[] defaultPalette = {new Color(134,75,51), new Color(250, 188, 137), new Color(254, 205, 126), new Color(255, 226, 134), new Color(255, 236, 131), new Color(255, 246, 143),
                                  new Color(237, 237, 125), new Color(213, 226, 134), new Color(182, 216, 132), new Color(143, 205, 158), new Color(130, 204, 181), new Color(119, 204, 201),
                                  new Color(143, 205, 185), new Color(130, 204, 181), new Color(119, 204, 201), new Color(106, 203, 222), new Color(107, 188, 233), new Color(113, 171, 221),
                                  new Color(124, 139, 198), new Color(135, 123, 185), new Color(153, 119, 180), new Color(189, 129, 183), new Color(219, 135, 185), new Color(234, 145, 189),
                                  new Color(244, 151, 169), new Color(246, 156, 155), new Color(248, 167, 146), new Color(248, 167, 146), new Color(249, 180, 138), new Color(2555, 255, 255)};
        
        public Color selectedColor = Color.Red;
        private Vector2 lastDrawPos;

        private Texture2D platformButtonTexture = GraphicsEngine.Instance.GetTexture("Editor/platformButton");
        private int platformButtonSize;
        private Texture2D paintButtonTexture = GraphicsEngine.Instance.GetTexture("Editor/paintButton");
        private int paintButtonSize;

        private bool expandedPalette = false;
        private Texture2D colorCircle = GraphicsEngine.Instance.GetTexture("Editor/colorCircle");
        private Texture2D smallColorCircle = GraphicsEngine.Instance.GetTexture("Editor/smallColorCircle");
        private Texture2D enclosedPaletteBorder = GraphicsEngine.Instance.GetTexture("Editor/enclosedPaletteBorder");
        private Texture2D expandPaletteBorder = GraphicsEngine.Instance.GetTexture("Editor/expandPaletteBorder");
        private Texture2D objectSelector = GraphicsEngine.Instance.GetTexture("Editor/objectSelector");

        private Texture2D[] depthSelectorTexture = new Texture2D[3];

        private Texture2D editorGradient = GraphicsEngine.Instance.GetTexture("Editor/editorGradient");

        private int brushSize = 20;
        private int SPACING = 7;
        #endregion

        #region Properties

        #endregion

        public LevelEditor()
        {
            platformButtonSize = (int)(platformButtonTexture.Width * 0.7);

            layerData = new Color[3][,][];
            layerEdited = new bool[3][,];
            layerTexture = new Texture2D[3][,];

            layerData[0] = new Color[mapSizeX, mapSizeY][];
            layerEdited[0] = new bool[mapSizeX, mapSizeY];
            layerTexture[0] = new Texture2D[mapSizeX, mapSizeY];

            layerData[1] = new Color[mapSizeX, mapSizeY][];
            layerEdited[1] = new bool[mapSizeX, mapSizeY];
            layerTexture[1] = new Texture2D[mapSizeX, mapSizeY];

            layerData[2] = new Color[mapSizeX, mapSizeY][];
            layerEdited[2] = new bool[mapSizeX, mapSizeY];
            layerTexture[2] = new Texture2D[mapSizeX, mapSizeY];

            for (int i = 0; i < mapSizeX; i++)
            {
                for (int j = 0; j < mapSizeY; j++)
                {
                    layerTexture[0][i, j] = TextureUtils.createBlankTexture(cellSize, cellSize, Color.White);
                    layerData[0][i, j] = new Color[cellSize * cellSize];
                    layerTexture[0][i, j].GetData<Color>(layerData[0][i, j]);
                    layerEdited[0][i, j] = false;

                    layerTexture[1][i, j] = TextureUtils.createBlankTexture(cellSize, cellSize, new Color(0, 0, 0, 0));
                    layerData[1][i, j] = new Color[cellSize * cellSize];
                    layerTexture[1][i, j].GetData<Color>(layerData[1][i, j]);
                    layerEdited[1][i, j] = false;

                    layerTexture[2][i, j] = TextureUtils.createBlankTexture(cellSize, cellSize, new Color(0, 0, 0, 0));
                    layerData[2][i, j] = new Color[cellSize * cellSize];
                    layerTexture[2][i, j].GetData<Color>(layerData[2][i, j]);
                    layerEdited[2][i, j] = false;
                }
            }

            platforms = new List<Platform>();

            depthSelectorTexture[0] = GraphicsEngine.Instance.GetTexture("Editor/depthSelectorBottom");
            depthSelectorTexture[1] = GraphicsEngine.Instance.GetTexture("Editor/depthSelectorMiddle");
            depthSelectorTexture[2] = GraphicsEngine.Instance.GetTexture("Editor/depthSelectorTop");
        }

        public void Save()
        {
            LevelData save = new LevelData();
            save.Platforms = platforms;
            XnaSerializer.Serialize<LevelData>(@"Content\Levels\Level.xml", save);
        }

        public override void Update(GameTime gameTime)
        {

            for (int i = 0; i < mapSizeX; i++)
            {
                for (int j = 0; j < mapSizeY; j++)
                {
                    for (int k = 0; k < 3; k++)
                    {
                        if (layerEdited[k][i, j])
                        {
                            layerTexture[k][i, j].GetData<Color>(layerData[k][i, j]);
                            layerEdited[k][i, j] = false;
                        }
                    }
                }
            }

            int x = (int)InputHandler.MousePosition.X;
            int y = (int)InputHandler.MousePosition.Y;

            if (InputHandler.KeyPressed(Microsoft.Xna.Framework.Input.Keys.F1))
                Save();

            if (InputHandler.KeyPressed(Microsoft.Xna.Framework.Input.Keys.Enter))
            {
                SceneManager.Destroy(gameObject);
                GameStateManager.LoadGame(new LevelData(platforms));
            }

            if (selectedTool == 0)
            {
                if (InputHandler.LeftMouseClicked())
                    drawing = true;
                if (InputHandler.LeftMouseUp())
                    drawing = false;
                if (expandedPalette)
                    drawing = false;
                if (drawing)
                {

                    if (Vector2.Distance(new Vector2(x, y), lastDrawPos) > SPACING)
                    {
                        if (Vector2.Distance(new Vector2(x, y), new Vector2(InputHandler.LastMouseState.X, InputHandler.LastMouseState.Y)) < 8)
                        {
                            UseBrush(x, y, Brush.Circle, selectedColor);
                            lastDrawPos = new Vector2(x, y);
                        }
                        else if (Math.Abs(x - InputHandler.LastMouseState.X) > Math.Abs(y - InputHandler.LastMouseState.Y))
                        {
                            int cx = InputHandler.LastMouseState.X;
                            while (Math.Abs(cx - x) > SPACING)
                            {
                                int cy = y + (int)((double)((double)(x - cx) / (double)(x - InputHandler.LastMouseState.X)) * (double)(InputHandler.LastMouseState.Y - y));

                                UseBrush(cx, cy, Brush.Circle, selectedColor);
                                lastDrawPos = new Vector2(cx, cy);

                                if (cx > x) cx -= SPACING;
                                else cx += SPACING;
                            }
                        }
                        else
                        {
                            int cy = InputHandler.LastMouseState.Y;
                            while (Math.Abs(cy - y) > SPACING)
                            {
                                int cx = x + (int)((double)((double)(y - cy) / (double)(y - InputHandler.LastMouseState.Y)) * (double)(InputHandler.LastMouseState.X - x));

                                UseBrush(cx, cy, Brush.Circle, selectedColor);
                                lastDrawPos = new Vector2(cx, cy);

                                if (cy > y) cy -= SPACING;
                                else cy += SPACING;
                            }
                        }
                    }

                }

                if (InputHandler.RightMouseClicked())
                {
                    TextureUtils.FloodFill(layerData[selectedLayer][x / cellSize, y / cellSize], x, y, selectedColor);
                    layerEdited[selectedLayer][x / cellSize, y / cellSize] = true;
                }
            }

            if (selectedTool == 1)
            {
                if (InputHandler.LeftMouseClicked())
                {
                    dragStart = InputHandler.MousePosition;
                }
                if (InputHandler.LeftMouseUp() && dragStart != Vector2.Zero)
                {
                    platforms.Add(new Platform(dragStart, InputHandler.MousePosition));
                }
            }
            else
            {
                dragStart = Vector2.Zero;
            }

            for (int i = 0; i < mapSizeX; i++)
            {
                for (int j = 0; j < mapSizeY; j++)
                {
                    for(int k = 0; k < 3; k++)
                        if (layerEdited[k][i, j])
                            layerTexture[k][i, j].SetData<Color>(layerData[k][i, j]);
                }
            }

            if (InputHandler.MouseWheelDown())
                brushSize--;
            if (InputHandler.MouseWheelUp())
                brushSize++;
            brushSize = (int) MathHelper.Clamp(brushSize, 1, 20);
            SPACING = brushSize / 2 + 1;

            Draw();
        }

        private void Draw()
        {
            for (int i = 0; i < mapSizeX; i++)
            {
                for (int j = 0; j < mapSizeY; j++)
                {
                    GraphicsEngine.Instance.DrawGUITexture(layerTexture[0][i, j], new Rectangle(i * cellSize, j * cellSize, cellSize, cellSize), null, Color.White, Properties.LAYER_GUI_02 + 0.005f);
                    GraphicsEngine.Instance.DrawGUITexture(layerTexture[1][i, j], new Rectangle(i * cellSize, j * cellSize, cellSize, cellSize), null, Color.White, Properties.LAYER_GUI_02);
                    GraphicsEngine.Instance.DrawGUITexture(layerTexture[2][i, j], new Rectangle(i * cellSize, j * cellSize, cellSize, cellSize), null, Color.White, Properties.LAYER_GUI_02 - 0.005f);
                }
            }

            foreach (Platform p in platforms)
            {
                Color drawColor = Color.White;
                if (Math.Abs((p.start.Y - p.end.Y) / (p.start.X - p.end.X)) > 1)
                    drawColor = Color.Red;
                GraphicsEngine.Instance.DrawLine(p.start, p.end, 4, Color.Black, Properties.LAYER_GUI_02 - 0.01f);
                GraphicsEngine.Instance.DrawLine(p.start, p.end, 2, drawColor, Properties.LAYER_GUI_02 - 0.02f);
            }
            if (selectedTool == 1)
            {
                if (InputHandler.LeftMouseDown())
                {
                    Color drawColor = Color.White;
                    if (Math.Abs((dragStart.Y - InputHandler.MousePosition.Y) / (dragStart.X - InputHandler.MousePosition.X)) > 1)
                        drawColor = Color.Red;
                    GraphicsEngine.Instance.DrawLine(dragStart, InputHandler.MousePosition, 4, Color.Black, Properties.LAYER_GUI_02 - 0.01f);
                    GraphicsEngine.Instance.DrawLine(dragStart, InputHandler.MousePosition, 2, drawColor, Properties.LAYER_GUI_02 - 0.02f);
                }
            }

            //GraphicsEngine.Instance.DrawGUITexture(editorGradient, new Rectangle(0, 0, GraphicsEngine.Instance.ScreenRectangle.Width, 140), null, Color.White, Properties.LAYER_GUI_02 - 0.01f);


            //------------------------------------------------draw the depth selector------------------------------------------------

            GraphicsEngine.Instance.DrawGUITexture(depthSelectorTexture[selectedLayer], new Rectangle(350, 18, 42, 114), null, Color.White, Properties.LAYER_GUI_02 - 0.03f);

            if (InputHandler.LeftMouseDown() && new Rectangle((int)InputHandler.MousePosition.X, (int)InputHandler.MousePosition.Y, 1, 1)
                            .Intersects(new Rectangle(350, 18, 42, 114 / 3)))
                selectedLayer = 2;

            if (InputHandler.LeftMouseDown() && new Rectangle((int)InputHandler.MousePosition.X, (int)InputHandler.MousePosition.Y, 1, 1)
                            .Intersects(new Rectangle(350, 18 + 114 / 3, 42, 114 / 3)))
                selectedLayer = 1;

            if (InputHandler.LeftMouseDown() && new Rectangle((int)InputHandler.MousePosition.X, (int)InputHandler.MousePosition.Y, 1, 1)
                            .Intersects(new Rectangle(350, 18 + 114 / 3 + 114 / 3, 42, 114 / 3)))
                selectedLayer = 0;

            //-------------------------------------------------draw the color palette------------------------------------------------

            int cWidth = colorCircle.Width;
            GraphicsEngine.Instance.DrawGUITexture(colorCircle, new Rectangle(18, 19, cWidth, cWidth), null, selectedColor, Properties.LAYER_GUI_01 - 0.03f);

            if (new Rectangle((int)InputHandler.MousePosition.X, (int)InputHandler.MousePosition.Y, 1, 1).Intersects(new Rectangle(18, 19, colorCircle.Width, colorCircle.Height)))
            {
                expandedPalette = true;
            }
            if (!new Rectangle((int)InputHandler.MousePosition.X, (int)InputHandler.MousePosition.Y, 1, 1).Intersects(new Rectangle(4, 4, expandPaletteBorder.Width, expandPaletteBorder.Height)))
            {
                expandedPalette = false;
            }
            if (expandedPalette)
            {
                GraphicsEngine.Instance.DrawGUITexture(expandPaletteBorder, new Rectangle(4, 4, expandPaletteBorder.Width, expandPaletteBorder.Height), null, Color.White, Properties.LAYER_GUI_01 - 0.02f);
                selectedColor = ColorPalette(defaultPalette);
            }
            else
            {
                GraphicsEngine.Instance.DrawGUITexture(enclosedPaletteBorder, new Rectangle(4, 4, enclosedPaletteBorder.Width, enclosedPaletteBorder.Height), null, Color.White, Properties.LAYER_GUI_01 - 0.02f);
            }
            GUIUtils.drawToolbarItem(platformButtonTexture, 114, 4);
            GUIUtils.drawToolbarItem(paintButtonTexture, 192, 4);

            if (selectedTool == 1 || new Rectangle((int)InputHandler.MousePosition.X, (int)InputHandler.MousePosition.Y, 1, 1).Intersects(new Rectangle(114, 4, 75, 74)))
            {
                if (InputHandler.LeftMouseClicked())
                    selectedTool = 1;
            }
            if (selectedTool == 0 || new Rectangle((int)InputHandler.MousePosition.X, (int)InputHandler.MousePosition.Y, 1, 1).Intersects(new Rectangle(192, 4, 75, 74)))
            {
                if (InputHandler.LeftMouseDown())
                    selectedTool = 0;
            }

            //-----------------------------------------------draw the object selector-------------------------------------------------

            GraphicsEngine.Instance.DrawGUITexture(objectSelector, new Rectangle(4, GraphicsEngine.Instance.ScreenRectangle.Height - 130, objectSelector.Width, objectSelector.Height), null, Color.White);
        }

        private void UseBrush(int x, int y, Brush b, Color c)
        {
            for (int i = x - brushSize; i < x + brushSize; i++)
            {
                for (int j = y - brushSize; j < y + brushSize; j++)
                {
                    if (i > 0 && j > 0 && i < cellSize * mapSizeX && j < cellSize * mapSizeY)
                    {


                        if (b == Brush.Circle)
                        {
                            if (Vector2.Distance(new Vector2(i, j), new Vector2(x, y)) < brushSize)
                            {
                                TextureUtils.SetPixel(i, j, layerData[selectedLayer][i / cellSize, j / cellSize], c, cellSize);
                                layerEdited[selectedLayer][i / cellSize, j / cellSize] = true;
                            }
                        }


                        if (b == Brush.Square)
                        {
                            TextureUtils.SetPixel(i, j, layerData[selectedLayer][i / cellSize, j / cellSize], c, cellSize);
                            layerEdited[selectedLayer][i / cellSize, j / cellSize] = true;
                        }
                    }
                }
            }
        }

        private Color ColorPalette(Color[] colors)
        {
            Color clickedColor = selectedColor;
            for (int j = 0; j < 5; j++)
            {
                for (int i = 0; i < 6; i++)
                {
                    int startXOffset = (j % 2 == 0) ? 128 : 156;
                    GraphicsEngine.Instance.DrawGUITexture(smallColorCircle, new Rectangle(startXOffset + (56 * i), 63 + (j * 41), smallColorCircle.Width, smallColorCircle.Height), null, colors[(6 * j) + i], Properties.LAYER_GUI_01 - 0.03f);
                    if (InputHandler.LeftMouseClicked() && new Rectangle((int)InputHandler.MousePosition.X, (int)InputHandler.MousePosition.Y, 1, 1).Intersects(new Rectangle(startXOffset + (56 * i), 63 + (j * 41), smallColorCircle.Width, smallColorCircle.Height)))
                    {
                        clickedColor = colors[(6 * j) + i];
                        expandedPalette = false;
                    }
                }
            }
            return clickedColor;
        }
    }
}
